#include "Routes.h"
#include "Util.h"
#include "Handlers.h"
#include "Link.h"
#include "XMLTree.h"
#include "HBSD.h"
#include "Node.h"

Routes::Routes(Handlers *router) 
{
	this->router = router;
	router = NULL;
}

Routes::~Routes()
{
	// learing the temporal List
	while(!temporalList.empty())
	{
		list<TemporalRoute * >::iterator iter = temporalList.begin();
		free(*iter);
		temporalList.erase(iter);
	}
	//  Clearing the links map
	routeMap.clear();

}


string Routes::eidFromURI(string uri) 
{
	int cnt = 0;
	int sz = uri.length();
	int indx = 0;
	for (indx=0; indx < sz; indx++) 
	{
		if (uri.at(indx) == '/') 
		{
			if (++cnt == 3) 
			{
				break;
			}
		}
	}
	return uri.substr(0, indx);
}


Link *Routes::getTemporalLink(string eid, Link *excludeLink, bool notAssociated) 
{
	
	for (list<TemporalRoute * >::iterator iter = temporalList.begin(); iter != temporalList.end();iter++) 
	{
		if ((*iter)->eid.compare(eid) == 0) 
		{
			if ((*iter)->link == excludeLink) 
			{
				continue;
			}
			if (!notAssociated) 
			{
				return (*iter)->link;
			}
			if (!(*iter)->link->isAssociated()) 
			{
				return (*iter)->link;
			}
		}
	}
	return NULL;
}

Link *Routes::getLink(string eid, Link *excludeLink)
{
	// Look through the temporal routes.

	for (list<TemporalRoute * >::iterator iter = temporalList.begin(); iter != temporalList.end();iter++) 
	{
		if ((*iter)->eid.compare(eid) == 0) 
		{
			if ((*iter)->link == excludeLink) 
			{
				continue;
			}
			if (!(*iter)->link->isAssociated()) 
			{
				return (*iter)->link;
			}
		}
	}

	// No temporal route; what about a persistent route?

	Link* link = NULL; 
	link = routeMap[eid];
	if (link == NULL) 
	{
		return NULL;
	}
	if (link != excludeLink) 
	{
		return link;
	}
	return NULL;
}


void Routes::deleteTemporalRoute(string eid, Link *link) 
{
	for (list<TemporalRoute * >::iterator iter = temporalList.begin(); iter != temporalList.end();iter++) 
	{
	
		if ((*iter)->eid.compare(eid) == 0) 
		{
			if ((*iter)->link == link) 
			{
				temporalList.erase(iter);
				if (HBSD::log->enabled(Logging::DEBUG))
				{
					HBSD::log->debug(string("Temporal route deleted: ")+ eid + string(" -> ") + (*iter)->eid);
				}
				break;
			}
		}
	}
}



void Routes::addTemporalRoute(string eid, Link *link) 
{
	TemporalRoute* tr = (TemporalRoute*)malloc(sizeof(TemporalRoute));
	tr->eid = eid;
	tr->link = link;
	temporalList.push_back(tr);
	tr = NULL;
	if (HBSD::log->enabled(Logging::DEBUG)) 
	{
		HBSD::log->debug(string("Temporal route created: ") +
				eid + string(" -> ") + link->id);
	}
}


void Routes::reportReceived(XMLTree* event)
{
	try 
	{
		// For each route in the report, construct a route_add_event
		// and have the route added.
		int num = event->numChildElements();
		for (int n=0; n<num; n++) 
		{
			map<string, string> m;
			XMLTree * routeAddEvt = new XMLTree(string("route_add_event"), m);
			routeAddEvt->addChildElement(event->getChildElement(n));
			addRoute(routeAddEvt);
		}
	} 
	catch (exception &e) 
	{
		if (HBSD::log->enabled(Logging::ERROR)) {
			HBSD::log->error(string("Ill-formed route_report received"));
		}
	}
}

void Routes::deleteRoute(XMLTree* evtRouteDelete) 
{
	try 
	{
		XMLTree* el = evtRouteDelete->getChildElementRequired(string("dest"));
		string eid = eidFromURI(el->getAttrRequired(string("uri")));
		// should be synchronized
		routeMap.erase(eid);
	} 
	catch (exception &e) 
	{
		if (HBSD::log->enabled(Logging::ERROR)) 
		{
			HBSD::log->error(string("Ill-formed route_delete_event"));
		}
	}
}

void Routes::addRoute(XMLTree* evtRouteAdd) 
{
	try 
	{
		XMLTree *el = evtRouteAdd->getChildElementRequired(string("route_entry"));
		string linkName = el->getAttrRequired(string("link"));
		el = el->getChildElementRequired(string("dest_pattern"));
		string eid = eidFromURI(el->getAttrRequired(string("uri")));
		Node* node = router->nodes->conditionalAdd(eid);
		Link* link = router->links->getById(linkName);
		if (link == NULL) 
		{
			if (HBSD::log->enabled(Logging::WARN)) 
			{
				// Normally we might log this as an error, but it can
				// occur due to the route_report. We need link_report
				// to support link_ids.
				HBSD::log->warn(string("Attempted to add a route using an unknown link: ")+ linkName + string(" (ignored)"));
			}
			return;
		}
		//synchronized(this) 
		{
			routeMap[eid] = link;
		}

		// Notify the Node of the association. This can result in a
		// request to open the link.
		node->routeDefined(link);
		if (HBSD::log->enabled(Logging::DEBUG)) 
		{
			HBSD::log->debug(string("Added route mapping EID ") + eid + string(" to link ") + linkName);
		}
		return;
	} 
	catch (exception & e) 
	{
		if (HBSD::log->enabled(Logging::ERROR)) 
		{
			HBSD::log->error(string("Ill-formed route_add_event"));
		}
		return;
	}
}
